package com.springboot.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

// Security配置
@Configuration
@EnableWebSecurity
// 开通方法授权
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired MyAuthenticationHandler authenticationHandler;
  @Autowired UserDetailServiceImpl userDetailService;

  // 密码加密器
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  // 配置认证管理器，授权模式为“poassword”时会用到
  @Bean
  public AuthenticationManager authenticationManager() throws Exception {
    return super.authenticationManager();
  }

  // 配置用户账号密码查询服务，密码编码器
  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder());
  }
  // 授权规则配置
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // 授权配置
    http.csrf().disable(); // 屏蔽跨域防护
    http.logout().permitAll(); // 放行登出
    // 允许表单登录，框架会开放/login端口，也可以手动设置登陆页
    http.formLogin()
        // .loginPage("/login.html")//此配置会替换/login接口,无权限时默认跳转到“login.html”。但是直接替换会出现问题，导致post请求的“/login”会404
        // 想要达到修改登录界面目的需要配置放行"/login.html"，然后前端控制无权限时跳转“/login.html”
        .successForwardUrl("/loginSuccess") // .successHandler()在后面对此处配置进行了覆盖
        .permitAll()
        .failureHandler(authenticationHandler)
        .successHandler(authenticationHandler);
    // 拒绝访问的处理器，权限
    http.exceptionHandling().accessDeniedHandler(authenticationHandler);
    // web授权，配置path和权限，支持"/**"，且前面会覆盖后面的配置，所以范围小的配置需要放在范围大的配置前面，
    // 例如：先配置的禁行"/dept/**"会覆盖后面配置的放行“/dept/list”
    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry =
        http.authorizeRequests();
    registry.antMatchers("/dept/list").permitAll();
    registry.antMatchers("/dept/**").authenticated();
    registry.antMatchers("/login", "/login.html").permitAll(); // 登录路径放行
    registry.anyRequest().authenticated(); // 其他路径都要拦截
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
  }
}
